'''
对backtrader进行改善优化：

一：继承创建手续费stampDutyCommissionScheme类，根据中国股市设置佣金最低额度5元，已经印花税卖出单边收取模式。

二：创建策略类Strategy_LJT：新策略可继承使用
1.策略中增加回测订单信息输出。
2.策略中增加成交结果信息输出。

三：创建整体运行函数
backtrader_run(code = "600000",
                   start="2020-01-01",
                   end = "2020-11-01",
                   strategy = Strategy_LJT,
                   data = None,
                   cash = 10000,
                   commission = 0.0002,
                   stamp_duty = 0.001,
                   )
'''
# 先引入后面可能用到的包（package）
import pandas as pd
from datetime import datetime
import backtrader as bt
import matplotlib.pyplot as plt
import backtrader.analyzers as bts
from backtrader.order import Order, BuyOrder, SellOrder
# %matplotlib inline
import pyautogui

pyautogui.FAILSAFE = False

from simplelivingfeed import SimpleLivingData
import numpy as np
import easytrader, time

# 正常显示画图时出现的中文和负号
from pylab import mpl

mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False


# stampdutycommission.py
# 用法：
# comminfo = stampDutyCommissionScheme(stamp_duty=0.001,commission=0.001)#实例化自定义佣金信息
# cerebro.broker.addcommissioninfo(comminfo)#添加佣金实例
class stampDutyCommissionScheme(bt.CommInfoBase):
    '''本佣金模式下，买入仅支付佣金，卖出支付佣金和印花税'''
    params = (
        ('stamp_duty', 0.001),  # 印花税，交易额的0.1%，卖出方收取;
        ('commission', 0.0002),  # 佣金，交易额的0.02%，起征点5元，双向收取;
        ('trans_rate', 0.00002),  # 过户费，交易额的0.002%，双向收取，忽略仅上海交易所6开头股票收取;
        ('stocklike', True),  #
        ('commtype', bt.CommInfoBase.COMM_FIXED),  # 确定数，非百分数
    )

    def _getcommission(self, size, price, pseudoexec):
        '''
        if size is greater than 0,this indicates a long / buying of shares.
        if size is less than 0,this indicates a short / selling of shares
        '''
        if size > 0:  # 买入
            comm_temp = size * price * self.p.commission
            return comm_temp if comm_temp > 5 else 5  # 佣金最低5元
        elif size < 0:  # 卖出
            comm_temp = - size * price * self.p.commission
            if comm_temp < 5:      comm_temp = 5  # 佣金最低5元##
            stamp_temp = - size * price * self.p.stamp_duty
            return comm_temp + stamp_temp

        else:
            return 0  # just in case for some reaon the size is 0 .


# 采用pyautogui进行网页下单操作，下单网页参考tradingview
class Ljtbroker(bt.BackBroker):
    '''
    改写提交订单函数，提交订单时同时触发鼠标键盘，进行订单提交
    改写订单执行逻辑，增加涨跌停处理
    '''

    def __init__(self):
        super().__init__()

        anchor_loc = pyautogui.locateCenterOnScreen('./loc_pic/anchor.png')
        assert (anchor_loc is not None)

        # calc button loc positions
        self.buy_but = (anchor_loc[0] - 50, anchor_loc[1] + 50)
        self.sell_but = (anchor_loc[0] - 180, anchor_loc[1] + 50)
        self.market_tab = (anchor_loc[0] - 210, anchor_loc[1] + 100)
        self.num_input = (anchor_loc[0] - 50, anchor_loc[1] + 175)

    def submit(self, order, check=True):
        super().submit(order, check)

        # click market anyway
        pyautogui.click(self.market_tab)
        size = str(abs(order.size))
        if 0 == order.ordtype:
            # buy order
            pyautogui.click(self.buy_but)
            pyautogui.click(self.num_input)
            pyautogui.press('backspace', presses=5)
            pyautogui.write(size)
            pyautogui.press('enter')
        else:
            # sell order
            pyautogui.click(self.sell_but)
            pyautogui.click(self.num_input)
            pyautogui.press('backspace', presses=5)
            pyautogui.write(size)
            pyautogui.press('enter')

    def _try_exec(self, order):
        data = order.data

        popen = getattr(data, 'tick_open', None)
        if popen is None:
            popen = data.open[0]
        phigh = getattr(data, 'tick_high', None)
        if phigh is None:
            phigh = data.high[0]
        plow = getattr(data, 'tick_low', None)
        if plow is None:
            plow = data.low[0]
        pclose = getattr(data, 'tick_close', None)
        if pclose is None:
            pclose = data.close[0]

        pcreated = order.created.price
        plimit = order.created.pricelimit

        # 本日一字涨停，买不了，一字跌停，卖不了
        if ((order.isbuy() and plow >= data.close[-1] * 1.09) or \
                (order.issell() and phigh <= data.close[-1] * 0.91)):
            order.reject()
            self.notify(order)
            print("交易失败提示：涨停，买不了，跌停，卖不了")

            return

        if order.exectype == Order.Market:
            self._try_exec_market(order, popen, phigh, plow)

        elif order.exectype == Order.Close:
            self._try_exec_close(order, pclose)

        elif order.exectype == Order.Limit:
            self._try_exec_limit(order, popen, phigh, plow, pcreated)

        elif (order.triggered and
              order.exectype in [Order.StopLimit, Order.StopTrailLimit]):
            self._try_exec_limit(order, popen, phigh, plow, plimit)

        elif order.exectype in [Order.Stop, Order.StopTrail]:
            self._try_exec_stop(order, popen, phigh, plow, pcreated, pclose)

        elif order.exectype in [Order.StopLimit, Order.StopTrailLimit]:
            self._try_exec_stoplimit(order,
                                     popen, phigh, plow, pclose,
                                     pcreated, plimit)

        elif order.exectype == Order.Historical:
            self._try_exec_historical(order)


# 采用pywinauto进行客户端下单操作，参考同花顺客户端
class Ljtbroker2(bt.BackBroker):
    '''
    改写提交订单函数，提交订单时同时触发鼠标键盘，进行订单提交
    改写订单执行逻辑，增加涨跌停处理
    '''
    params = (
        ('code', '600000'),
    )

    def __init__(self):
        super().__init__()
        import easytrader
        # self.user = easytrader.use('htzq_client')
        # self.user = easytrader.use('yh_client')#银河
        self.user = easytrader.use('ths')  # 同花
        self.user.connect(r'C:\同花顺软件\同花顺\xiadan.exe')  # 类似 r'C:\htzqzyb2\xiadan.exe'
        self.user.enable_type_keys_for_editor()
        print(self.user.position)
        # print(self.user.balance)
        self.account = self.user.balance  # 同花
        # self.account = self.user.balance[0]#银河
        print(self.account)
        print(f"设置金额{self.account['可用金额']}")
        self.setcash(self.account['可用金额'])
        print(self.user.today_trades)
        print(self.user.today_entrusts)
        # user.buy('162411', price=10.15, amount=100)
        # user.sell('162411', price=1.55, amount=100)

    def submit(self, order, check=True):
        super().submit(order, check)
        size = str(abs(order.size))
        price = getattr(order.data, 'tick_close', None)
        # print(order.data._name)
        # print(f"self.p.code={self.p.code},order.size={order.size},price={price}")
        if 0 == order.ordtype:
            # buy order
            self.user.buy(order.data._name, price=price, amount=size)
        else:
            # sell order
            self.user.sell(order.data._name, price=price, amount=size)

    def _try_exec(self, order):
        data = order.data

        popen = getattr(data, 'tick_open', None)
        if popen is None:
            popen = data.open[0]
        phigh = getattr(data, 'tick_high', None)
        if phigh is None:
            phigh = data.high[0]
        plow = getattr(data, 'tick_low', None)
        if plow is None:
            plow = data.low[0]
        pclose = getattr(data, 'tick_close', None)
        if pclose is None:
            pclose = data.close[0]

        pcreated = order.created.price
        plimit = order.created.pricelimit

        # 本日一字涨停，买不了，一字跌停，卖不了
        if ((order.isbuy() and plow >= data.close[-1] * 1.09) or \
                (order.issell() and phigh <= data.close[-1] * 0.91)):
            order.reject()
            self.notify(order)
            print("交易失败提示：涨停，买不了，跌停，卖不了")

            return

        if order.exectype == Order.Market:
            self._try_exec_market(order, popen, phigh, plow)

        elif order.exectype == Order.Close:
            self._try_exec_close(order, pclose)

        elif order.exectype == Order.Limit:
            self._try_exec_limit(order, popen, phigh, plow, pcreated)

        elif (order.triggered and
              order.exectype in [Order.StopLimit, Order.StopTrailLimit]):
            self._try_exec_limit(order, popen, phigh, plow, plimit)

        elif order.exectype in [Order.Stop, Order.StopTrail]:
            self._try_exec_stop(order, popen, phigh, plow, pcreated, pclose)

        elif order.exectype in [Order.StopLimit, Order.StopTrailLimit]:
            self._try_exec_stoplimit(order,
                                     popen, phigh, plow, pclose,
                                     pcreated, plimit)

        elif order.exectype == Order.Historical:
            self._try_exec_historical(order)


# 使用tushare旧版接口获取数据
import tushare as ts


def get_day_data(code, autype='qfq', start='2010-01-01', end='2020-03-31'):
    df = ts.get_k_data(code, autype='qfq', start=start, end=end)
    df.index = pd.to_datetime(df.date)
    df['openinterest'] = 0
    df = df[['open', 'high', 'low', 'close', 'volume', 'openinterest']]
    return df


def get_tick_data(code, autype ='qfq', start='2010-01-01', end='2020-03-31'):
    df = ts.get_tick_data(code, date=end, src='tt')
    df.index = pd.to_datetime(end +' ' +df.time)
    df['open'] = df['price'].shift(1)
    df['high'] = np.where(df['price'] > df['open'], df['price'], df['open'])
    df['low'] = np.where(df['price'] < df['open'], df['price'], df['open'])
    df['close'] = df['price']
    df['openinterest'] = 0
    df = df[['open', 'high', 'low', 'close', 'volume', 'openinterest']]
    return df


class Strategy_LJT(bt.Strategy):
    # 全局设定交易策略的参数
    params = (
        ('maperiod', 20),
    )

    def __init__(self):
        # 指定价格序列
        self.dataclose = self.datas[0].close
        # 初始化交易指令、买卖价格和手续费
        self.order = None
        self.buyprice = None
        self.buycomm = None

        # 添加移动均线指标，内置了talib模块
        self.sma = bt.indicators.SimpleMovingAverage(
            self.dataclose, period=self.params.maperiod)

    def next(self):
        if self.order:  # 检查是否有指令等待执行,
            print(self.order)
            return
        # 检查是否持仓
        if not self.position:  # 没有持仓
            # 执行买入条件判断：收盘价格上涨突破20日均线
            if self.dataclose[0] > self.sma[0]:
                # 执行买入
                self.order = self.buy(size=100)
                self.log(
                    f"$$$$$$买入信号,收盘：{self.dataclose[0]}，现金={round(self.broker.getcash(), 2)}，资产={round(self.broker.getvalue(), 2)}，买入:{self.order.size}股")

        else:
            # 执行卖出条件判断：收盘价格跌破20日均线
            if self.dataclose[0] < self.sma[0]:
                # 执行卖出
                self.order = self.sell(size=100)
                self.log(
                    f"######卖出信号，收盘：{self.dataclose[0]}，卖出{self.order.size}股，现金={round(self.broker.getcash(), 2)}，总资产={round(self.broker.getvalue(), 2)}")

    # 交易记录日志（可省略，默认不输出结果）
    def log(self, txt, dt=None, doprint=True):
        if doprint:
            dt = dt or self.datas[0].datetime.datetime(0)
            print(f'{dt.isoformat()},{txt}')

    # 记录交易执行情况（可省略，默认不输出结果）
    def notify_order(self, order):
        # 如果order为submitted/accepted,返回空
        if order.status in [order.Submitted, order.Accepted]:
            # self.log('order.status in [order.Submitted, order.Accepted]')

            return
        # 如果order为buy/sell executed,报告价格结果
        # self.log(f"open：{self.datas[0].open[0]},high{self.datas[0].high[0]}，low{self.datas[0].low[0]}，close{self.datas[0].close[0]}")
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    f'下单买入{order.data._name}价格:{order.executed.price},数量:{order.size}股，成本:{order.executed.value},手续费:{round(order.executed.comm, 3)},\
                    出账：{round(abs(order.executed.price * order.size) + order.executed.comm, 2)}，现金={round(self.broker.getcash(), 2)}，总资产={round(self.broker.getvalue(), 2)}')

            else:
                self.log(
                    f'下单卖出{order.data._name}价格：{order.executed.price},数量:{order.size}股，成本: {order.executed.value},手续费:{round(order.executed.comm, 3)},\
                    入账={round(abs(order.executed.price * order.size) - order.executed.comm, 2)}，现金={round(self.broker.getcash(), 2)}，总资产={round(self.broker.getvalue(), 2)}')
            self.bar_executed = len(self)
            # 如果指令取消/交易失败, 报告结果
        elif order.status in [order.Canceled, order.Rejected]:
            self.log('订单被取消或被拒绝！！！')
        elif order.status is order.Margin:
            self.log('交易失败,现金不足！！！')

        self.order = None

    # 记录交易收益情况（可省略，默认不输出结果）
    def notify_trade(self, trade):
        if not trade.isclosed:
            return
        # self.log(f'策略收益：毛收益 {trade.pnl:.2f}, 净收益 {trade.pnlcomm:.2f}')
        print('-----------------------------回合记录---------------------------------')
        print("1: Data Code:                            {}".format(trade.data._name))
        print("2: Bar Num:                              {}".format(len(trade.data)))
        print("3: Current date:                         {}".format(self.data.datetime.date()))
        print('4: 状态:                                  Trade Complete')
        print('5: 回合标签Ref:                            {}'.format(trade.ref))
        print('6: 当期净收益PnL:                          {}'.format(round(trade.pnl, 2)))
        print('--------------------------------------------------------------------')

    # 回测结束后输出结果（可省略，默认输出结果）
    def stop(self):
        self.log('回测结束， 期末现金 %.2f，总资产 %.2f ' %
                 (self.broker.getcash(), self.broker.getvalue()), doprint=True)


def quant_trader_run(code=["600000"], start="2018-01-01", end="2020-11-01", strategy=Strategy_LJT, broker=bt.BackBroker,
                     data="back_test", cash=200000, commission=0.0002, stamp_duty=0.001):
    # 回测期间
    start_data = pd.to_datetime(start)  # datetime(2020, 1, 1)
    end_data = pd.to_datetime(end)  # datetime(2020, 2, 20)
    start_data_string = start_data.strftime('%Y-%m-%d')
    end_data_string = end_data.strftime('%Y-%m-%d')

    # 初始化cerebro回测系统设置
    cerebro = bt.Cerebro()
    # 设置初始资本为10,000
    cerebro.broker.setcash(cash)
    # 加载数据    # 将数据传入回测系统
    if data == "back_test":
        for code_i in code:
            dataframe = get_day_data(code_i, autype='qfq', start=start_data_string,
                                     end=end_data_string)  # 使用tushare获取浦发银行（代码：600000）数据。
            data = bt.feeds.PandasData(dataname=dataframe, fromdate=start_data, todate=end_data)
            cerebro.adddata(data, name=code_i)
    elif data == "back_test_tick":
            for code_i in code:
                dataframe = get_tick_data(code_i, autype='qfq', start=start_data_string,
                                         end=end_data_string)  # 使用tushare获取浦发银行（代码：600000）数据。
                data = bt.feeds.PandasData(dataname=dataframe, timeframe=bt.TimeFrame.Seconds)
                cerebro.adddata(data, name=code_i)
    else:
        # 设置经纪行
        if broker == Ljtbroker2:
            # cerebro.setbroker(broker(code = code))#带入代码
            cerebro.setbroker(broker())  # 不带入代码
        else:
            cerebro.setbroker(broker())
        # feed = SimpleLivingData('ixic', timeframe=bt.TimeFrame.Seconds, compression=30)
        # feed = SimpleLivingData('hkHSI', timeframe=bt.TimeFrame.Seconds, compression=4)
        # feed = SimpleLivingData('szzs', timeframe=bt.TimeFrame.Seconds, compression=4)
        # feed = SimpleLivingData('nyhj', symbol='nyhj', sleeptime = 5,timeframe=bt.TimeFrame.Seconds, compression=10)
        for code_i in code:
            feed = SimpleLivingData(code_i, datasource=data, timeframe=bt.TimeFrame.Seconds, compression=3)
            cerebro.adddata(feed, name=code_i)
    # 将交易策略加载到回测系统中
    cerebro.addstrategy(strategy)
    # 添加分析指标
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, riskfreerate=0.01, annualize=True, _name='sharp_ratio')
    cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='annual_return')
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='TradeAnalyzer')
    cerebro.addanalyzer(bt.analyzers.SharpeRatio_A)

    # 设置交易手续费为 0.1%
    # cerebro.broker.setcommission(commission=0.001, margin=5, automargin=True)
    comminfo = stampDutyCommissionScheme(stamp_duty=stamp_duty, commission=commission)
    cerebro.broker.addcommissioninfo(comminfo)
    print(f'初始资金: {cerebro.broker.getvalue()} 回测期间：{start_data_string}:{end_data_string}')

    # cerebro.broker.set_coc(False)  # 设置以当日收盘价成交

    # 运行回测系统
    thestrats = cerebro.run()
    # 获取回测结束后的总资金
    portvalue = cerebro.broker.getvalue()
    pnl = portvalue - cash
    # 打印结果
    print(f'总资产: {round(portvalue, 3)}，现金: {round(cerebro.broker.get_cash(), 3)}')
    print(f'净收益: {round(pnl, 3)}')
    # 输出分析器结果字典
    thestrat = thestrats[0]
    # print(thestrats[0])
    # print('夏普率:',thestrat.analyzers.sharp_ratio.get_analysis())
    print('年度收益:', thestrat.analyzers.annual_return.get_analysis())
    # print('回撤表:',thestrat.analyzers.drawdown.get_analysis())
    # #进一步从字典中取出需要的值
    print('夏普率(风险收益比):', (thestrat.analyzers.sharp_ratio.get_analysis()['sharperatio']), '%')
    print('最大回撤:', (thestrat.analyzers.drawdown.get_analysis()['max']['drawdown']), '%')
    # print('胜利次数:', thestrat.analyzers.TradeAnalyzer.get_analysis()['won']['total']
    #                 , '盈利', thestrat.analyzers.TradeAnalyzer.get_analysis()['won']['pnl']['total'])
    # print('失败次数:', thestrat.analyzers.TradeAnalyzer.get_analysis()['lost']['total']
    #                 , '损失', thestrat.analyzers.TradeAnalyzer.get_analysis()['lost']['pnl']['total'])
    # #打印各个分析者内容
    # for a in thestrat.analyzers:
    #     a.print()
    cerebro.plot(style='candle')
    # cerebro.plot(style='candlestick',scheme=Tradimo())
    # from backtrader_plotting import Bokeh
    # from backtrader_plotting.schemes import Tradimo
    # b = Bokeh(style='bar', plot_mode='single', scheme=Tradimo())
    # cerebro.plot(b)


###########################################以下是测试用例########################################################
class my_strategy(Strategy_LJT):
    # 全局设定交易策略的参数
    params = (('symbol', 10),)

    def __init__(self):
        # 指定价格序列
        self.dataclose = self.datas[0].close
        self.datavolume = self.datas[0].volume
        # 初始化交易指令、买卖价格和手续费
        self.order = None
        self.buyprice = None
        self.buycomm = None
        # print(f"初始金额：{self.broker.getcash()}")
        # 添加移动均线指标，内置了talib模块
        # self.sma = bt.indicators.SimpleMovingAverage(
        #     self.dataclose, period=self.params.maperiod)

    def next(self):  # 策略运行逻辑
        length = len(self)
        for d in self.datas:  # 支持多股票同时交易
            self.log(
                f'{d._name}实时股价:close={d.close[0]},open={d.open[0]},high={d.high[0]},low={d.low[0]},volume={d.volume[0]},len={length}')
            if 1 == length % 5:  # 简单买入卖出测试策略
                self.log('step1')
                self.buy(data=d, size=300)
            elif 2 == length % 5:
                self.log('step2')
                self.sell(data=d, size=100)
            elif 3 == length % 5:
                self.log('step3')
                self.sell(data=d, size=200)
            elif 4 == length % 5:
                self.log('step4')
                self.buy(data=d, size=100)
            elif 0 == length % 5:
                self.log('step0')
                self.sell(data=d, size=100)

    def notify_trade(self, trade):
        pass


class GridStrategy(Strategy_LJT):
    params = (
        ("printlog", True),
        ("top", 33),
        ("bottom", 17),
        ("gp", 0.05),  # 网格百分比grid_percent
    )

    def __init__(self):
        self.mid = (self.p.top + self.p.bottom) / 2.0
        # 百分比区间计算
        # 这里多1/2，是因为arange函数是左闭右开区间。
        perc_level = [x for x in np.arange(1 + self.p.gp * 5, 1 - self.p.gp * 5 - self.p.gp / 2, -self.p.gp)]
        # 价格区间
        # print(self.mid)
        self.price_levels = [self.mid * x for x in perc_level]
        # 记录上一次穿越的网格
        self.last_price_index = None
        # 总手续费
        self.comm = 0.0
        self.dataclose = self.datas[0].close
        # 剩余资金
        self.rest_cash = self.broker.startingcash
        # 持仓金额
        self.total_hold = 0.0
        # 持仓数量
        self.hold_pos = 0

        self.order = None

    def next(self):
        # self.log('Close, %.2f' % self.dataclose[0])
        # 如果有未决订单，则不判断
        if self.order:
            return
        # print(self.last_price_index)
        # 开仓
        if self.last_price_index == None:
            print("b", len(self.price_levels))
            for i in range(len(self.price_levels)):
                price = self.data.close[0]
                # print("c", i, price, self.price_levels[i][0])
                if self.data.close[0] > self.price_levels[i] and self.data.close[0] < self.price_levels[0]:
                    self.last_price_index = i
                    target = i / (len(self.price_levels) - 1)
                    target *= self.broker.getvalue()
                    possize = self.getposition(self.data, self.broker).size
                    if not target and possize:  # closing a position
                        self.order = self.close(data=self.data, size=possize, price=price)
                    else:
                        value = self.broker.getvalue(datas=[self.data])
                        comminfo = self.broker.getcommissioninfo(self.data)
                        price = self.data.close[0]
                        if target > value:
                            size = comminfo.getsize(price, target - value)
                            size = int(size / 100) * 100 if size > 100 else 100
                            self.order = self.buy(size=size, price=price)

                        elif target < value:
                            size = comminfo.getsize(price, value - target)
                            size = int(size / 100) * 100 if size > 100 else 100
                            self.order = self.sell(size=size, price=price)
                    # self.order_target_percent(target=i/(len(self.price_levels) - 1))
                    print("a")
                    return
        # 调仓
        else:
            signal = False
            while True:
                upper = None
                lower = None
                if self.last_price_index > 0:
                    upper = self.price_levels[self.last_price_index - 1]
                if self.last_price_index < len(self.price_levels) - 1:
                    lower = self.price_levels[self.last_price_index + 1]
                # 还不是最轻仓，继续涨，再卖一档
                if upper != None and self.data.close > upper:
                    self.last_price_index = self.last_price_index - 1
                    signal = True
                    continue
                # 还不是最重仓，继续跌，再买一档
                if lower != None and self.data.close < lower:
                    self.last_price_index = self.last_price_index + 1
                    signal = True
                    continue
                break
            if signal:
                self.long_short = None
                target = self.last_price_index / (len(self.price_levels) - 1)
                target *= self.broker.getvalue()
                price = self.data.close[0]
                possize = self.getposition(self.data, self.broker).size
                if not target and possize:  # closing a position
                    self.order = self.close(data=self.data, size=possize, price=price)
                else:
                    value = self.broker.getvalue(datas=[self.data])
                    comminfo = self.broker.getcommissioninfo(self.data)
                    price = self.data.close[0]
                    if target > value:
                        size = comminfo.getsize(price, target - value)
                        # 订单数目低于100则改为100，是否可以不执行？int(size/100)*100 if size>100 else return
                        size = int(size / 100) * 100 if size > 100 else 100
                        self.order = self.buy(size=size, price=price)
                    elif target < value:
                        size = comminfo.getsize(price, value - target)
                        # 订单数目低于100则改为100，是否可以不执行？int(size/100)*100 if size>100 else return
                        size = int(size / 100) * 100 if size > 100 else 100
                        self.order = self.sell(size=size, price=price)
                # self.order_target_percent(target=self.last_price_index/(len(self.price_levels) - 1))

    # # 输出交易记录
    # def log(self, txt, dt=None, doprint=False):
    #     if self.params.printlog or doprint:
    #         dt = dt or self.datas[0].datetime.date(0)
    #         print('%s, %s' % (dt.isoformat(), txt))
    #
    # def notify_order(self, order):
    #     # 有交易提交/被接受，啥也不做
    #     if order.status in [order.Submitted, order.Accepted]:
    #         return
    #     # 交易完成，报告结果
    #     if order.status in [order.Completed]:
    #         if order.isbuy():
    #             self.log('执行买入, 价格: %.2f, 数量：%.2f,成本: %.2f, 手续费 %.2f' %
    #                      (order.executed.price, order.executed.size, order.executed.value, order.executed.comm))
    #             # self.buyprice = order.executed.price
    #             self.comm += order.executed.comm
    #             self.hold_pos += order.executed.size
    #             self.total_hold = order.executed.price * self.hold_pos
    #             self.rest_cash -= order.executed.price * order.executed.size
    #             self.rest_cash -= order.executed.comm
    #             self.log('剩余资金: %.2f, 仓位：%.2f,总成本: %.2f, 总手续费 %.2f' %
    #                      (self.rest_cash, self.hold_pos, self.total_hold, self.comm))
    #         else:
    #             self.log('执行卖出, 价格: %.2f, 数量：%.2f,成本: %.2f, 手续费 %.2f' %
    #                      (order.executed.price, order.executed.size, order.executed.value, order.executed.comm))
    #             self.comm += order.executed.comm
    #             self.hold_pos += order.executed.size
    #             self.total_hold = order.executed.price * self.hold_pos
    #             self.rest_cash -= order.executed.price * order.executed.size
    #             self.rest_cash -= order.executed.comm
    #             self.log('剩余资金: %.2f, 仓位：%.2f,总成本: %.2f, 总手续费 %.2f' %
    #                      (self.rest_cash, self.hold_pos, self.total_hold, self.comm))
    #     elif order.status in [order.Canceled, order.Margin, order.Rejected]:
    #         self.log("交易失败")
    #     self.order = None
    #
    # # 输出手续费
    # def stop(self):
    #     self.log("手续费:%.2f 成本比例:%.5f" % (self.comm, self.comm / self.broker.getvalue()))
    #     self.log("总资金:%.2f " % (self.broker.getvalue()))
    #     self.total_hold = self.data.close[0] * self.hold_pos
    #     self.log('剩余资金: %.2f, 仓位：%.2f,总成本: %.2f, 总手续费 %.2f' %
    #              (self.rest_cash, self.hold_pos, self.total_hold, self.comm))


class my_strategy2(Strategy_LJT):
    # 全局设定交易策略的参数
    params = (
        ('maperiod', 10),
    )

    def __init__(self):
        # 指定价格序列
        self.dataclose = self.datas[0].close

    def next(self):

        length = len(self)
        for d in self.datas:
            self.log(
                f'{d._name}:close={d.close[0]},preclose={d.close[-1]},open={d.open[0]},high={d.high[0]},low={d.low[0]},volume={d.volume[0]},len={length}')

            # if (d.volume[0]-d.volume[-1]) > 500 and d.close[0] > d.close[-1]:
            if (d.volume[0]) < 1000 and d.close[0] < d.close[-1]:
                self.buy(data=d, size=100)
            if self.position:  # 有
                # if (d.volume[0]-d.volume[-1]) > 500 and d.close[0] < d.close[-1]:
                if (d.volume[0]) > 1000000 and d.close[0] < d.close[-1]:
                    self.sell(data=d, size=100)


class my_strategy3(Strategy_LJT):
    '''
    长线熊市触底：下跌趋势中，250.120.30连续缩量，5日放量，当日缩量
    '''
    # 全局设定交易策略的参数
    params = (
        ('maperiod', 10),
    )

    def __init__(self):
        self.broker.set_coc(True)  # 设置以当日收盘价成交
        # 添加移动均线指标，内置了talib模块
        for d in self.datas:
            d.price = d.close
            d.price_sma250 = bt.indicators.SimpleMovingAverage(
                d.price, period=250)
            d.price_sma120 = bt.indicators.SimpleMovingAverage(
                d.price, period=120)
            d.price_sma30 = bt.indicators.SimpleMovingAverage(
                d.price, period=30)
            d.price_sma5 = bt.indicators.SimpleMovingAverage(
                d.price, period=5)
            d.volume_sma5 = bt.indicators.SimpleMovingAverage(
                d.volume, period=5)
            d.volume_sma30 = bt.indicators.SimpleMovingAverage(
                d.volume, period=30)
            d.volume_sma120 = bt.indicators.SimpleMovingAverage(
                d.volume, period=120)
            d.volume_sma250 = bt.indicators.SimpleMovingAverage(
                d.volume, period=250)

    def next(self):
        length = len(self)
        for d in self.datas:
            # self.log(
            #     f'{d._name}:close={d.close[0]},preclose={d.close[-1]},open={d.open[0]},high={d.high[0]},low={d.low[0]},volume={d.volume[0]},len={length}')
            # if (d.volume[0]-d.volume[-1]) > 500 and d.close[0] > d.close[-1]:
            if d.volume[0] \
                    and d.volume[0] < d.volume_sma30[0] < d.volume_sma120[0] < d.volume_sma250[0] < d.volume_sma5[0] \
                    and d.price[0] < d.price_sma120[0] \
                    and d.price[0] < d.price_sma30[0] \
                    and d.price[0] < d.price_sma120[0] \
                    and d.price[0] < d.price_sma5[0]:
                # self.buy(data=d, size=10)
                self.buy_bracket(data=d, size=self.broker.getcash() / d.price[0] / 10, price=d.price[0],
                                 stopprice=d.price[0] * 0.85, limitprice=d.price[0] * 1.15)
                self.log(
                    f'下单{d._name}:close={d.close[0]},preclose={d.close[-1]},open={d.open[0]},high={d.high[0]},low={d.low[0]},volume={d.volume[0]},len={length}')
            # if self.position:  # 有
            #     if d.volume_sma30[0]*2 < d.volume[0]\
            #             and d.price_sma30[0] < d.price[0] :
            #         self.close(data=d)


class my_strategy4(Strategy_LJT):
    '''
    短线缩量下跌：当日量价小于30，5日均线，
    '''
    # 全局设定交易策略的参数
    params = (
        ('maperiod', 10),
    )

    def __init__(self):
        self.broker.set_coc(True)  # 设置以当日收盘价成交
        # 添加移动均线指标，内置了talib模块
        for d in self.datas:
            d.price = d.close
            d.price_sma250 = bt.indicators.SimpleMovingAverage(
                d.price, period=250)
            d.price_sma120 = bt.indicators.SimpleMovingAverage(
                d.price, period=120)
            d.price_sma30 = bt.indicators.SimpleMovingAverage(
                d.price, period=30)
            d.price_sma5 = bt.indicators.SimpleMovingAverage(
                d.price, period=5)
            d.volume_sma5 = bt.indicators.SimpleMovingAverage(
                d.volume, period=5)
            d.volume_sma30 = bt.indicators.SimpleMovingAverage(
                d.volume, period=30)
            d.volume_sma120 = bt.indicators.SimpleMovingAverage(
                d.volume, period=120)
            d.volume_sma250 = bt.indicators.SimpleMovingAverage(
                d.volume, period=250)

    def next(self):

        length = len(self)
        for d in self.datas:
            # self.log(
            #     f'{d._name}:close={d.close[0]},preclose={d.close[-1]},open={d.open[0]},high={d.high[0]},low={d.low[0]},volume={d.volume[0]},len={length}')
            # if (d.volume[0]-d.volume[-1]) > 500 and d.close[0] > d.close[-1]:
            if d.volume[0] < d.volume_sma30[0] \
                    and d.volume[0] < d.volume_sma5[0] \
                    and d.price[0] < d.price_sma30[0] \
                    and d.price[0] < d.price_sma5[0]:
                # self.buy(data=d, size=10)
                self.buy_bracket(data=d, size=self.broker.getcash() / d.price[0] / 10, price=d.price[0],
                                 stopprice=d.price[0] * 0.85, limitprice=d.price[0] * 1.15)
                self.log(
                    f'下单{d._name}:close={d.close[0]},preclose={d.close[-1]},open={d.open[0]},high={d.high[0]},low={d.low[0]},volume={d.volume[0]},len={length}')
            # if self.position:  # 有
            #     if d.volume_sma30[0]*2 < d.volume[0]\
            #             and d.price_sma30[0] < d.price[0] :
            #         self.close(data=d)

    # 记录交易收益情况（可省略，默认不输出结果）
    # def notify_trade(self, trade):
    #     pass


class my_strategy5(Strategy_LJT):
    '''
    短线缩量小于5日线一半，买入：1/5现金，止盈1.5，止损0.75，夏普率(风险收益比): 0.44
    '''
    # 全局设定交易策略的参数
    params = (('maperiod', 10),)

    def __init__(self):
        self.broker.set_coc(True)  # 设置以当日收盘价成交
        # 添加移动均线指标，内置了talib模块
        for d in self.datas:
            d.price = d.close
            d.price_sma250 = bt.indicators.SimpleMovingAverage(
                d.price, period=250)
            d.price_sma120 = bt.indicators.SimpleMovingAverage(
                d.price, period=120)
            d.price_sma30 = bt.indicators.SimpleMovingAverage(
                d.price, period=30)
            d.price_sma5 = bt.indicators.SimpleMovingAverage(
                d.price, period=5)
            d.volume_sma5 = bt.indicators.SimpleMovingAverage(
                d.volume, period=5)
            d.volume_sma30 = bt.indicators.SimpleMovingAverage(
                d.volume, period=30)
            d.volume_sma120 = bt.indicators.SimpleMovingAverage(
                d.volume, period=120)
            d.volume_sma250 = bt.indicators.SimpleMovingAverage(
                d.volume, period=250)

    def next(self):
        length = len(self)
        for d in self.datas:
            # self.log(
            #     f'{d._name}:close={d.close[0]},preclose={d.close[-1]},open={d.open[0]},high={d.high[0]},low={d.low[0]},volume={d.volume[0]},len={length}')
            # if (d.volume[0]-d.volume[-1]) > 500 and d.close[0] > d.close[-1]:
            if d.volume[0] \
                    and d.volume[0] < d.volume_sma5[0] * 0.5 \
                    and d.price[0] \
                    and d.price[0]:
                # self.buy(data=d, size=10)
                self.buy_bracket(data=d, size=self.broker.getcash() / d.price[0] / 5, price=d.price[0],
                                 stopprice=d.price[0] * 0.75, limitprice=d.price[0] * 1.5)
                self.log(
                    f'下单{d._name}:close={d.close[0]},preclose={d.close[-1]},open={d.open[0]},high={d.high[0]},low={d.low[0]},volume={d.volume[0]},len={length}')


class my_strategy6(Strategy_LJT):
    '''
    短线日内交易，放量大于三倍跟单，止盈止损0.005
    '''
    # 全局设定交易策略的参数
    params = (('maperiod', 10),)

    def __init__(self):
        self.broker.set_coc(True)  # 设置以当日收盘价成交
        # 添加移动均线指标，内置了talib模块
        for d in self.datas:
            d.price = d.close
            # d.price_sma250 = bt.indicators.SimpleMovingAverage(
            #     d.price, period=250)
            # d.price_sma120 = bt.indicators.SimpleMovingAverage(
            #     d.price, period=120)
            d.price_sma30 = bt.indicators.SimpleMovingAverage(
                d.price, period=30)
            d.price_sma5 = bt.indicators.SimpleMovingAverage(
                d.price, period=5)
            d.volume_sma5 = bt.indicators.SimpleMovingAverage(
                d.volume, period=5)
            d.volume_sma30 = bt.indicators.SimpleMovingAverage(
                d.volume, period=30)
            # d.volume_sma120 = bt.indicators.SimpleMovingAverage(
            #     d.volume, period=120)
            # d.volume_sma250 = bt.indicators.SimpleMovingAverage(
            #     d.volume, period=250)

    def next(self):
        length = len(self)
        for d in self.datas:
            if d.volume[0] \
                    and d.volume[0] > 50000 \
                    and d.price[0] > d.open[0] \
                    and d.price[0]:
                self.buy_bracket(data=d, size=self.broker.getcash() / d.price[0] / 1.1, price=d.price[0],
                                 stopprice=d.price[0] * 0.995, limitprice=d.price[0] * 1.005)
                # self.log(
                #     f'下单{d._name}:close={d.close[0]},preclose={d.close[-1]},open={d.open[0]},high={d.high[0]},low={d.low[0]},volume={d.volume[0]},len={length}')
            if d.volume[0] \
                    and d.volume[0] > 50000 \
                    and d.price[0] < d.open[0] \
                    and d.price[0]:
                self.sell_bracket(data=d, size=self.broker.getcash() / d.price[0] / 1.1, price=d.price[0],
                                 stopprice=d.price[0] * 1.005, limitprice=d.price[0] * 0.995)


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    quant_trader_run(
        # code=["399995", "601818", "600000", "165525"],  # 股票组合
        code=["512000"],
        start="2010-01-01", end="2021-01-19",  # 模拟回测开始截止日期
        strategy=my_strategy6,  # 选择策略
        data="back_test_tick",  # 数据类型分为back_test,back_test_tick回测, "living"实盘数据，"tick_sim"单日TIC级模拟，"day_sim"，历史模拟数据收盘可继续模拟测试
        # broker=Ljtbroker2,  # 选择交易方式:不填为模拟交易，Ljtbroker1为网页交易，Ljtbroker2为客户端交易
        cash=10000,  # 模拟交易初始资金，若网页交易和客户端交易是则此处失效，系统自动获取账号资金。
        commission=0.0002,  # 佣金万分2
        # trans_rate=0.00002,  # 交易费率
        stamp_duty=0.000)  # 印花税
